////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2003-2006 Adobe Macromedia Software LLC and its licensors.
//  All Rights Reserved. The following is Source Code and is subject to all
//  restrictions on such code as contained in the End User License Agreement
//  accompanying this product.
//
////////////////////////////////////////////////////////////////////////////////

package mx.charts.chartClasses
{

import flash.display.DisplayObject;
import flash.geom.Point;
import flash.geom.Rectangle;
import mx.charts.AxisRenderer;
import mx.charts.GridLines;
import mx.charts.LinearAxis;
import mx.charts.chartClasses.IAxisRenderer;
import mx.charts.styles.HaloDefaults;
import mx.collections.ArrayCollection;
import mx.collections.ICollectionView;
import mx.collections.IList;
import mx.collections.ListCollectionView;
import mx.collections.XMLListCollection;
import mx.core.UIComponent;
import mx.core.mx_internal;
import mx.graphics.SolidColor;
import mx.graphics.Stroke;
import mx.styles.CSSStyleDeclaration;
import mx.styles.StyleManager;

use namespace mx_internal;

//--------------------------------------
//  Styles
//--------------------------------------

/**
 *  The name of the CSS class selector to use
 *  when formatting titles on the axes.
 */
[Style(name="axisTitleStyleName", type="String", inherit="yes")]

/**
 *  The class selector that defines the style properties
 *  for the default grid lines.
 *  If you explicitly set the <code>backgroundElements</code> property
 *  on your chart, this value is ignored.
 */
[Style(name="gridLinesStyleName", type="String", inherit="no")]

/**
 *  The size of the region, in pixels, between the bottom
 *  of the chart data area and the bottom of the chart control.
 */
[Style(name="gutterBottom", type="Number", format="Length", inherit="no")]

/**
 *  The size of the region, in pixels, between the left
 *  of the chart data area and the left of the chart control.
 */
[Style(name="gutterLeft", type="Number", format="Length", inherit="no")]

/**
 *  The size of the region, in pixels, between the right
 *  side of the chart data area and the outside of the chart control.
 */
[Style(name="gutterRight", type="Number", format="Length", inherit="no")]

/**
 *  The size of the region, in pixels, between the top
 *  of the chart data area and the top of the chart control.
 */
[Style(name="gutterTop", type="Number", format="Length", inherit="no")]

/**
 *  The class selector that defines the style properties
 *  for the horizontal axis.
 */
[Style(name="horizontalAxisStyleName", type="String", inherit="no")]

/**
 *  The class selector that defines the style properties
 *  for the second horizontal axis.
 */
[Style(name="secondHorizontalAxisStyleName", type="String", inherit="no")]

/**
 *  The class selector that defines the style properties
 *  for the second vertical axis.
 */
[Style(name="secondVerticalAxisStyleName", type="String", inherit="no")]

/**
 *  Alignment of text within a container.
 *  Valid values are <code>"left"</code>, <code>"right"</code>,
 *  and <code>"center"</code>.
 *  
 *  @default "left"
 */
[Style(name="textAlign", type="String", enumeration="left,center,right", inherit="yes")]

/**
 *  The class selector that defines the style properties
 *  for the vertical axis.
 */
[Style(name="verticalAxisStyleName", type="String", inherit="no")]

/**
 *  The CartesianChart class is a base class for the common chart types.
 *  CartesianChart defines the basic layout behavior of the standard
 *  rectangular, two-dimensional charts.
 *
 *  @mxml
 *  
 *  <p>The <code>&lt;mx:CartesianChart&gt;</code> tag inherits all the
 *  properties of its parent classes and adds the following properties:</p>
 *  
 *  <pre>
 *  &lt;mx:CartesianChart
 *    <strong>Properties</strong>
 *    computedGutters="<i>No default</i>"
 *    dataRegion="<i>Rectangle; no default</i>"
 *    horizontalAxis="<i>Axis; no default</i>"
 *    horizontalAxisRatio=".33"
 *    horizontalAxisRenderer="<i>AxisRenderer; no default</i>"
 *    secondDataProvider="<i>No default</i>" 
 *    secondHorizontalAxis="<i>Axis; no default</i>" 
 *    secondHorizontalAxisRenderer="<i>AxisRenderer; no default</i>" 
 *    secondSeries="<i>Series; no default</i>"
 *    secondVerticalAxis="<i>Axis; no default</i>"
 *    secondVerticalAxisRenderer="<i>AxisRenderer; no default</i>"
 *    verticalAxis="<i>Axis; no default</i>"
 *    verticalAxisRenderer="<i>AxisRenderer; no default</i>"  
 *   
 *    <strong>Styles</strong>  
 *    axisTitleStyleName="<i>Style; no default</i>"
 *    gridLinesStyleName="<i>Style; no default</i>"
 *    gutterBottom="<i>No default</i>"
 *    gutterLeft="<i>No default</i>"
 *    gutterRight="<i>No default</i>"
 *    gutterTop="<i>No default</i>"
 *    horizontalAxixStyle="<i>Style; no default</i>"
 *    secondHorizontalAxixStyle="<i>Style; no default</i>"
 *    secondVerticalAxisStyleName="<i>Style; no default</i>"
 *    textAlign="left|right|center"
 *    verticalAxisStyleName="<i>Style; no default</i>"
 *  /&gt;
 *  </pre>
 *  
 *  @see mx.charts.CategoryAxis
 *  @see mx.charts.LinearAxis
 *  @see mx.charts.chartClasses.ChartBase
 */
public class CartesianChart extends ChartBase
{
    include "../../core/Version.as";

	//--------------------------------------------------------------------------
	//
	//  Class initialization
	//
	//--------------------------------------------------------------------------

	/**
	 *  @private
	 */
	private static var stylesInited:Boolean = initStyles();

	/**
	 *  @private
	 */
	private static function initStyles():Boolean
	{
		HaloDefaults.init();

		var cartesianChartStyle:CSSStyleDeclaration =
			HaloDefaults.createSelector("CartesianChart");

		cartesianChartStyle.defaultFactory = function():void
		{
			this.axisColor = 0xD5DEDD;
			this.chartSeriesStyles = HaloDefaults.chartBaseChartSeriesStyles;
			this.dataTipRenderer = DataTip;
			this.fill = new SolidColor(0xFFFFFF, 0);
			this.calloutStroke = new Stroke(0x888888,2);			
			this.fontFamily = "Arial";
			this.fontSize = 10;
			this.horizontalAxisStyleName = "blockCategoryAxis";
			this.secondHorizontalAxisStyleName = "blockCategoryAxis";
			this.secondVerticalAxisStyleName = "blockNumericAxis";
			this.verticalAxisStyleName = "blockNumericAxis";
		}

		return true;
	}

	//--------------------------------------------------------------------------
	//
	//  Constructor
	//
	//--------------------------------------------------------------------------

	/**
	 *  Constructor.
	 */
	public function CartesianChart()
	{
		super();

		StyleManager.registerInheritingStyle("axisTitleStyleName");

		horizontalAxisRenderer = new AxisRenderer();
		verticalAxisRenderer = new AxisRenderer();
	
		horizontalAxis = new LinearAxis();
		verticalAxis = new LinearAxis();
		
		_series2 = _userSeries2 = [];
		transforms = [new CartesianTransform()];
		
		var gridLines:GridLines = new GridLines();
		backgroundElements = [ gridLines ];
		_defaultGridLines = gridLines;
	}

	//--------------------------------------------------------------------------
	//
	//  Variables
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @private
	 */
	private var _transformBounds:Rectangle = new Rectangle();
	
	/**
	 *  @private
	 */
	private var _computedGutters:Rectangle = new Rectangle();

	/**
	 *  @private
	 */
	private var _bAxisLayoutDirty:Boolean = true;
	
	/**
	 *  @private
	 */
	private var _bgridLinesStyleNameDirty:Boolean = true;
	
	/**
	 *  @private
	 */
	private var _defaultGridLines:GridLines;
	
	/**
	 *  @private
	 */
	private var _bAxisStylesDirty:Boolean = true;
	
	/**
	 *  @private
	 */
	private var _bAxesRenderersDirty:Boolean = false;

	/**
	 *  @private
	 */
	private var _bDualMode:Boolean = false;
	
	/**
	 *  @private
	 */
	private var _labelElements2:Array;
	
	//--------------------------------------------------------------------------
	//
	//  Overridden properties: ChartBase
	//
	//--------------------------------------------------------------------------

	//----------------------------------
	//  backgroundElements
	//----------------------------------

	/**
	 *  @private
	 */
	override public function set backgroundElements(value:Array):void
	{
		super.backgroundElements = value;
		
		_defaultGridLines = null;
	}

	//----------------------------------
	//  chartState
	//----------------------------------

	/**
	 *  @private
	 */
	override protected function setChartState(value:uint):void
	{
		if (chartState == value)
			return;

		var oldState:uint = chartState;

		super.setChartState(value);

		if (_horizontalAxisRenderer)
			_horizontalAxisRenderer.chartStateChanged(oldState, value);
		if (_verticalAxisRenderer)
			_verticalAxisRenderer.chartStateChanged(oldState, value);

		if (_horizontalAxisRenderer2)
			_horizontalAxisRenderer2.chartStateChanged(oldState, value);
		if (_verticalAxisRenderer2)
			_verticalAxisRenderer2.chartStateChanged(oldState, value);

	}
	
	//----------------------------------
	//  dataRegion
	//----------------------------------

	/**
	 *  @inheritDoc
	 */
	override protected function get dataRegion():Rectangle
	{
		return _transformBounds;
	}

	//--------------------------------------------------------------------------
	//
	//  Properties
	//
	//--------------------------------------------------------------------------
    
	//----------------------------------
	//  computedGutters
	//----------------------------------

	[Inspectable(environment="none")]

	/**
	 *  The current computed size of the gutters of the CartesianChart.
	 *  The gutters represent the area between the padding
	 *  and the data area of the chart, where the titles and axes render.
	 *  By default, the gutters are computed dynamically.
	 *  You can set explicit values through the gutter styles.
	 *  The gutters are computed to match any changes to the chart
	 *  when it is validated by the LayoutManager.
	 */
	public function get computedGutters():Rectangle
	{
		return _computedGutters;
	}

	//----------------------------------
	//  horizontalAxis
	//----------------------------------

	/**
	 *  @private
	 *  Storage for the horizontalAxis property.
	 */
	private var _horizontalAxis:IAxis;
	
    [Inspectable(category="Data")]

	/**
	 *  Defines the labels, tick marks, and data position
	 *  for items on the x-axis.
	 *  Use either the LinearAxis class or the CategoryAxis class
	 *  to set the properties of the horizontalAxis as a child tag in MXML
	 *  or create a LinearAxis or CategoryAxis object in ActionScript.
	 */
	public function get horizontalAxis():IAxis
	{
		return _horizontalAxis;
	}
	
	/**
	 *  @private
	 */
	public function set horizontalAxis(value:IAxis):void
	{
		_horizontalAxis = value;
		_bAxesRenderersDirty = true;

		invalidateData();
		invalidateProperties();
	}

	//----------------------------------
	//  horizontalAxisRatio
	//----------------------------------

	[Inspectable(category="Data")]
	
	/**
	 *  Determines the height limit of the horiztonal axis.
	 *  The limit is the width of the axis times this ratio.
	 *
	 *  @default 0.33.
	 */
	public var horizontalAxisRatio:Number = 0.33;
	
	//----------------------------------
	//  horizontalAxisRenderer
	//----------------------------------

	/**
	 *  @private
	 *  Storage for the horizontalAxisRenderer property.
	 */
	private var _horizontalAxisRenderer:IAxisRenderer;
	
	[Inspectable(category="Data")]
	
	/**
	 *  Specifies how data appears along the x-axis of a chart.
	 *  Use the AxisRenderer class to define the properties
	 *  for horizontalAxisRenderer as a child tag in MXML
	 *  or create an AxisRenderer object in ActionScript.
	 */
	public function get horizontalAxisRenderer():IAxisRenderer
	{
		return _horizontalAxisRenderer;
	}

	/**
	 *  @private
	 */
	public function set horizontalAxisRenderer(value:IAxisRenderer):void
	{
		if (_horizontalAxisRenderer)
		{
			if (DisplayObject(_horizontalAxisRenderer).parent == this)
				removeChild(DisplayObject(_horizontalAxisRenderer));
			_horizontalAxisRenderer.otherAxes = (null);
		}

		_horizontalAxisRenderer = value;
		_horizontalAxisRenderer.horizontal = true;
		invalidateProperties();

		_bAxesRenderersDirty = true;
		_bAxisStylesDirty=true;

		invalidateChildOrder();
		invalidateProperties();
	}

	//----------------------------------
	//  secondDataProvider
	//----------------------------------
	
	/**
	 *  @private
	 *  Storage for the secondDataProvider property.
	 */
	private var _dataProvider2:ICollectionView;

	[Inspectable(category="Data")]
	
	/**
	 *  The second dataProvider for this chart.
	 *  The second dataProvider is typically rendered by a secondSeries.
	 */
	public function get secondDataProvider():Object
	{
		return _dataProvider2;
	}

	/**
	 *  @private
	 */
	public function set secondDataProvider(value:Object):void
	{
		if (value is Array)
		{
			value = new ArrayCollection(value as Array);
		}
		else if (value is ICollectionView)
		{
		}
		else if (value is IList)
		{
			value = new ListCollectionView(value as IList);
		}
		else if (value is XMLList)
		{
			value = new XMLListCollection(XMLList(value));
		}
		else if (value != null)
		{
			value = new ArrayCollection([ value ]);
		}
		else
		{
			value = new ArrayCollection();
		}
		
		_dataProvider2 = ICollectionView(value);

		if (!_bDualMode)
			initSecondaryMode();

		invalidateData();
	}

	//----------------------------------
	//  secondHorizontalAxis
	//----------------------------------

	/**
	 *  @private
	 *  Storage for the secondHorizontalAxis property.
	 */
	private var _horizontalAxis2:IAxis;
	
	[Inspectable(category="Data")]
	
	/**
	 *  Defines the labels, tick marks, and data position
	 *  for items on the y-axis.
	 *  Use either the LinearAxis class or the CategoryAxis class
	 *  to set the properties of the horizontalAxis as a child tag in MXML
	 *  or create a LinearAxis or CategoryAxis object in ActionScript.
	 */
	public function get secondHorizontalAxis():IAxis
	{
		return _horizontalAxis2;
	}

	/**
	 *  @private
	 */
	public function set secondHorizontalAxis(value:IAxis):void
	{
		_horizontalAxis2 = value;

		if (!_bDualMode)
			initSecondaryMode();

		_bAxesRenderersDirty = true;
		invalidateData();
		invalidateProperties();
	}
    
	//----------------------------------
	//  secondHorizontalAxisRenderer
	//----------------------------------

	/**
	 *  @private
	 *  Storage for the secondHorizontalAxisRenderer property.
	 */
	private var _horizontalAxisRenderer2:IAxisRenderer;
	
    [Inspectable(category="Data")]
	
	/**
	 *  Specifies how data appears along the y-axis of a chart.
	 *  Use the AxisRenderer class to set the properties
	 *  for verticalAxisRenderer as a child tag in MXML
	 *  or create an AxisRenderer object in ActionScript.
	 */
	public function get secondHorizontalAxisRenderer():IAxisRenderer
	{
		return _horizontalAxisRenderer2;
	}

	/**
	 *  @private
	 */
	public function set secondHorizontalAxisRenderer(value:IAxisRenderer):void
	{
		if (_horizontalAxisRenderer2)
		{
			if (DisplayObject(_horizontalAxisRenderer2).parent == this)
				removeChild(DisplayObject(_horizontalAxisRenderer2));
			_horizontalAxisRenderer2.otherAxes = null;
		}
		_horizontalAxisRenderer2 = value;

		if (!_bDualMode)
			initSecondaryMode();

		if (_horizontalAxisRenderer2)
			_horizontalAxisRenderer2.horizontal = true;


		invalidateProperties();

		_bAxesRenderersDirty = true;
		_bAxisStylesDirty=true;
		invalidateChildOrder();
		invalidateProperties();
	}

	//----------------------------------
	//  secondSeries
	//----------------------------------

	/**
	 *  @private
	 *  Storage for the secondSeries property.
	 */
	private var _series2:Array;
	
	/**
	 *  @private
	 */
	private var _userSeries2:Array;
	
    [Inspectable(category="Data", arrayType="mx.charts.chartClasses.Series")]

	/**
	 *  An array of Series objects that define the secondary chart data.
	 *  Secondary Series are displayed in the same data area as the primary 
	 *  chart series, but are typically rendered with different scales and axes.  
	 */
	public function get secondSeries():Array
	{
		return _series2;
	}

	/**
	 *  @private
	 */
	public function set secondSeries(value:Array):void
	{

		value = value == null ? [] : value;
		_userSeries2 = value;

		if (!_bDualMode)
			initSecondaryMode();

		invalidateSeries();
		invalidateData();

		legendDataChanged();
	}

	//----------------------------------
	//  secondVerticalAxis
	//----------------------------------

	/**
	 *  @private
	 *  Storage for the secondVerticalAxis property.
	 */
	private var _verticalAxis2:IAxis;
	
    [Inspectable(category="Data")]

	/**
	 *  The second vertical axis definition for this chart.
	 *  This class is typically used to render the axis for a secondSeries.
	 */
	public function get secondVerticalAxis():IAxis
	{
		return _verticalAxis2;
	}
	
	/**
	 *  @private
	 */
	public function set secondVerticalAxis(value:IAxis):void
	{
		_verticalAxis2 = value;

		if (!_bDualMode)
			initSecondaryMode();

		_bAxesRenderersDirty = true;
		invalidateData();
		invalidateProperties();
	}

	//----------------------------------
	//  secondVerticalAxisRenderer
	//----------------------------------

	/**
	 *  @private
	 *  Storage for the secondVerticalAxisRenderer property.
	 */
	private var _verticalAxisRenderer2:IAxisRenderer;
	
	[Inspectable(category="Data")]
	
	/**
	 *  Defines the labels, tick marks, and data position
	 *  for items on the x-axis.
	 *  Use either the LinearAxis class or the CategoryAxis class
	 *  to set the properties of the horizontalAxis as a child tag in MXML
	 *  or create a LinearAxis or CategoryAxis object in ActionScript.
	 */
	public function get secondVerticalAxisRenderer():IAxisRenderer
	{
		return _verticalAxisRenderer2;
	}

	/**
	 *  @private
	 */
	public function set secondVerticalAxisRenderer(value:IAxisRenderer):void
	{

		if (_verticalAxisRenderer2)
			if (DisplayObject(_verticalAxisRenderer2).parent == this)
				removeChild(DisplayObject(_verticalAxisRenderer2));

		_verticalAxisRenderer2 = value;

		_verticalAxisRenderer2.horizontal = false;
		invalidateProperties();

		_bAxisStylesDirty=true;

		_bAxesRenderersDirty = true;
		invalidateChildOrder();
		invalidateProperties();
	}
    
	//----------------------------------
	//  verticalAxis
	//----------------------------------

	/**
	 *  @private
	 *  Storage for the verticalAxis property.
	 */
	private var _verticalAxis:IAxis;

    [Inspectable(category="Data")]

	/**
	 *  Defines the labels, tick marks, and data position
	 *  for items on the y-axis.
	 *  Use either the LinearAxis class or the CategoryAxis class
	 *  to set the properties of the horizontalAxis as a child tag in MXML
	 *  or create a LinearAxis or CategoryAxis object in ActionScript.
	 */
	public function get verticalAxis():IAxis
	{
		return _verticalAxis;
	}
	
	/**
	 *  @private
	 */
	public function set verticalAxis(value:IAxis):void
	{
		_verticalAxis = value;
		_bAxesRenderersDirty = true;

		invalidateData();
		invalidateChildOrder();
		invalidateProperties();
	}

	//----------------------------------
	//  verticalAxisRatio
	//----------------------------------
	
	[Inspectable(category="Data")]
	
	/**
	 *  Determines the width limit of the vertical axis.
	 *  The limit is the width of the axis times this ratio.
	 *
	 *  @default 0.33.
	 */
	public var verticalAxisRatio:Number = 0.33;

	//----------------------------------
	//  verticalAxisRenderer
	//----------------------------------
	
	/**
	 *  @private
	 *  Storage for the verticalAxisRenderer property.
	 */
	private var _verticalAxisRenderer:IAxisRenderer;
	
    [Inspectable(category="Data")]

	/**
	 *  Specifies how data appears along the y-axis of a chart.
	 *  Use the AxisRenderer class to set the properties
	 *  for verticalAxisRenderer as a child tag in MXM
	 *  or create an AxisRenderer object in ActionScript.
	 */
	public function get verticalAxisRenderer():IAxisRenderer
	{
		return _verticalAxisRenderer;
	}

	/**
	 *  @private
	 */
	public function set verticalAxisRenderer(value:IAxisRenderer):void
	{
		if (_verticalAxisRenderer)
			if (DisplayObject(_verticalAxisRenderer).parent == this)
				removeChild(DisplayObject(_verticalAxisRenderer));

		_verticalAxisRenderer = value;

		_verticalAxisRenderer.horizontal = false;
		invalidateProperties();

		_bAxisStylesDirty=true;

		_bAxesRenderersDirty = true;
		invalidateProperties();
	}

	//--------------------------------------------------------------------------
	//
	//  Overridden methods: UIComponent
	//
	//--------------------------------------------------------------------------

	/**
	 *  @inheritDoc
	 */
	override protected function commitProperties():void
	{
		if (_bAxesRenderersDirty)
		{
			var addIndex:int = dataTipLayerIndex - 1;
			
			addChild(DisplayObject(_horizontalAxisRenderer));
			
			addChild(DisplayObject(_verticalAxisRenderer));
			
			if (_horizontalAxisRenderer2)
				addChild(DisplayObject(_horizontalAxisRenderer2));
			
			if (_verticalAxisRenderer2)
				addChild(DisplayObject(_verticalAxisRenderer2));

			invalidateDisplayList();

			if (_transforms)
			{
				CartesianTransform(_transforms[0]).setAxis(
					CartesianTransform.HORIZONTAL_AXIS, _horizontalAxis);
				
				CartesianTransform(_transforms[0]).setAxis(
					CartesianTransform.VERTICAL_AXIS, _verticalAxis);

				if (_transforms.length > 1)
				{
					CartesianTransform(_transforms[1]).setAxis(
						CartesianTransform.HORIZONTAL_AXIS,
						_horizontalAxis2 == null ?
						_horizontalAxis :
						_horizontalAxis2);
					
					CartesianTransform(_transforms[1]).setAxis(
						CartesianTransform.VERTICAL_AXIS,
						_verticalAxis2 == null ?
						_verticalAxis :
						_verticalAxis2);
				}
			}

			_horizontalAxisRenderer.axis = _horizontalAxis;
			_verticalAxisRenderer.axis = _verticalAxis;

			if (_horizontalAxisRenderer2)
			{
				_horizontalAxisRenderer2.axis = _horizontalAxis2 == null ?
												_horizontalAxis :
												_horizontalAxis2;
			}

			if (_verticalAxisRenderer2)
			{
				if (_verticalAxis2 == null)
					_verticalAxisRenderer2.axis = _verticalAxis;
				else
					_verticalAxisRenderer2.axis = _verticalAxis2;
			}

			var otherAxes:Array = [];
			if (_verticalAxisRenderer)
				otherAxes.push(_verticalAxisRenderer);
			if (_verticalAxisRenderer2)
				otherAxes.push(_verticalAxisRenderer2);
			
			if (_horizontalAxisRenderer)
				_horizontalAxisRenderer.otherAxes = otherAxes;
			
			if (_horizontalAxisRenderer2)
				_horizontalAxisRenderer2.otherAxes = otherAxes;

			_bAxesRenderersDirty = false;
		}

		if (_bAxisStylesDirty)
		{
			if (_horizontalAxisRenderer is DualStyleObject)
			{
				DualStyleObject(_horizontalAxisRenderer).internalStyleName =
					getStyle("horizontalAxisStyleName");
			}

			if (_verticalAxisRenderer is DualStyleObject)
			{
				DualStyleObject(_verticalAxisRenderer).internalStyleName =
					getStyle("verticalAxisStyleName");
			}

			if (_horizontalAxisRenderer2 is DualStyleObject)
			{
				DualStyleObject(_horizontalAxisRenderer2).internalStyleName =
					getStyle("secondHorizontalAxisStyleName");
			}
			if (_verticalAxisRenderer2 is DualStyleObject)
			{
				DualStyleObject(_verticalAxisRenderer2).internalStyleName =
					getStyle("secondVerticalAxisStyleName");
			}

			_bAxisStylesDirty = false;
		}

		if (_bgridLinesStyleNameDirty)
		{
			if (_defaultGridLines)
			{
				_defaultGridLines.internalStyleName =
					getStyle("gridLinesStyleName");
			}
			_bgridLinesStyleNameDirty = false;
		}

		super.commitProperties();
	}

	/**
	 *  @inheritDoc
	 */
	override protected function updateDisplayList(unscaledWidth:Number,
												  unscaledHeight:Number):void
	{
		super.updateDisplayList(unscaledWidth, unscaledHeight);

		updateAxisLayout(unscaledWidth, unscaledHeight);
	}

	/**
	 *  @inheritDoc
	 */
	override public function styleChanged(styleProp:String):void
	{
		if (styleProp == null ||
			_horizontalAxisRenderer != null &&
			styleProp == "horizontalAxisStyleName")
		{
			_bAxisStylesDirty = true;
			invalidateDisplayList();
		}

		if (styleProp == null ||
			_verticalAxisRenderer != null &&
			styleProp == "verticalAxisStyleName")
		{
			_bAxisStylesDirty = true;
			invalidateDisplayList();
		}

		if (styleProp == null ||
			_horizontalAxisRenderer2 != null &&
			styleProp == "secondHorizontalAxisStyleName")
		{
			_bAxisStylesDirty = true;
			invalidateDisplayList();
		}

		if (styleProp == null ||
			_verticalAxisRenderer2 != null &&
			styleProp == "secondVerticalAxisStyleName")
		{
			_bAxisStylesDirty = true;
			invalidateDisplayList();
		}

		if (styleProp == null ||
			_defaultGridLines != null && styleProp == "gridLinesStyleName")
		{
			_bgridLinesStyleNameDirty = true;
			invalidateDisplayList();
		}

		if (styleProp == null || styleProp.indexOf("gutter") == 0)
		{
			_bAxisLayoutDirty = true;
			invalidateDisplayList();
		}

		super.styleChanged(styleProp);
	}

	//--------------------------------------------------------------------------
	//
	//  Overridden methods: ChartBase
	//
	//--------------------------------------------------------------------------

	/**
	 *  @inheritDoc
	 */
	override mx_internal function updateData():void
	{
		if (dataProvider != null)
			applyDataProvider(ICollectionView(dataProvider),_transforms[0]);

		if (_dataProvider2 != null && _transforms.length >= 2)
			applyDataProvider(_dataProvider2,_transforms[1]);
	}

	/**
	 *  @inheritDoc
	 */
	override mx_internal function updateSeries():void
	{
		var displayedSeries:Array = applySeriesSet(series,_transforms[0]);
		
		if (_userSeries2 != null && _transforms.length >= 2) 
			_series2 = applySeriesSet(_userSeries2,_transforms[1]);

		var i:int;
		var len:int = displayedSeries ? displayedSeries.length : 0;
		var c:DisplayObject;
		var g:IChartElement;
		var labelLayer:UIComponent;

		removeElements(_backgroundElementHolder, true);
		removeElements(_seriesFilterer, false);
		removeElements(_annotationElementHolder, true);

		addElements(backgroundElements, _transforms[0], _backgroundElementHolder);
		allElements = backgroundElements.concat();
		
		addElements(displayedSeries,_transforms[0], _seriesFilterer);
		allElements = allElements.concat(displayedSeries);
		
		addElements(_series2,_transforms[1], _seriesFilterer);
		allElements = allElements.concat(_series2);

		labelElements = [];
		
		for (i = 0; i < displayedSeries.length; i++) 
		{
			g = displayedSeries[i] as IChartElement;
			if (!g)
				continue;
			
			labelLayer = UIComponent(g.labelContainer);
			if (labelLayer) 
				labelElements.push(labelLayer);				
		}
		
		for (i = 0; i < _series2.length; i++) 
		{
			g = _series2[i] as IChartElement;
			if (!g)
				continue;
			
			labelLayer = UIComponent(g.labelContainer);
			if (labelLayer) 
				labelElements.push(labelLayer);				
		}
		
		addElements(labelElements,_transforms[0],_annotationElementHolder);
		allElements = allElements.concat(labelElements);

		addElements(annotationElements,_transforms[0],_annotationElementHolder);
		allElements = allElements.concat(annotationElements);

		_transforms[0].elements = annotationElements.concat(displayedSeries).
										concat(backgroundElements);
		
		if (_transforms.length >= 2)
			_transforms[1].elements = _series2;

		invalidateData();
		invalidateSeriesStyles();
	}

	/**
	 *  @inheritDoc
	 */
	override mx_internal function updateAxisOrder(nextIndex:int):int
	{
		setChildIndex(DisplayObject(_horizontalAxisRenderer), nextIndex++);
		
		setChildIndex(DisplayObject(_verticalAxisRenderer), nextIndex++);

		if (_horizontalAxisRenderer2)
			setChildIndex(DisplayObject(_horizontalAxisRenderer2), nextIndex++);
		
		if (_verticalAxisRenderer2)
			setChildIndex(DisplayObject(_verticalAxisRenderer2), nextIndex++);
		
		return nextIndex;
	}

	/**
	 *  @inheritDoc
	 */
	override public function dataToLocal(... dataValues):Point
	{
		var data:Object = {};
		var da:Array = [ data ];
		var n:int = dataValues.length;
		
		if (n > 0)
		{
			data["d0"] = dataValues[0];
			_transforms[0].getAxis(CartesianTransform.HORIZONTAL_AXIS).
				mapCache(da, "d0", "v0");
		}
		
		if (n > 1)
		{
			data["d1"] = dataValues[1];
			_transforms[0].getAxis(CartesianTransform.VERTICAL_AXIS).
				mapCache(da, "d1", "v1");			
		}

		_transforms[0].transformCache(da,"v0","s0","v1","s1");
		
		return new Point(data.s0 + _transformBounds.left,
						 data.s1 + _transformBounds.top);
	}

	/**
	 *  @inheritDoc
	 */
	override public function localToData(v:Point):Array
	{
		var values:Array = _transforms[0].invertTransform(
											v.x - _transformBounds.left,
											v.y - _transformBounds.top);
		return values;
	}

	//--------------------------------------------------------------------------
	//
	//  Methods
	//
	//--------------------------------------------------------------------------
	
	override protected function measure():void
	{
		super.measure();
		measuredMinHeight = _horizontalAxisRenderer.minHeight + 40;
		measuredMinWidth = _verticalAxisRenderer.minWidth + 40;
		if(_horizontalAxisRenderer2 != null)
			measuredMinHeight += _horizontalAxisRenderer2.minWidth;
		if(_verticalAxisRenderer2 != null)
			measuredMinWidth += _verticalAxisRenderer2.minWidth;
		
	}
	

	/**
	 *  private
	 */
	private function updateAxisLayout(unscaledWidth:Number,
									  unscaledHeight:Number):void
	{
		var paddingLeft:Number = getStyle("paddingLeft");
		var paddingRight:Number = getStyle("paddingRight");
		var paddingTop:Number = getStyle("paddingTop");
		var paddingBottom:Number = getStyle("paddingBottom");

		var gutterLeft:Object = getStyle("gutterLeft");
		var gutterRight:Object = getStyle("gutterRight");
		var gutterTop:Object = getStyle("gutterTop");
		var gutterBottom:Object = getStyle("gutterBottom");

		if (!isNaN(horizontalAxisRatio))
		{
			_horizontalAxisRenderer.heightLimit =
				horizontalAxisRatio * unscaledHeight;
		}

		if (!isNaN(verticalAxisRatio))
		{
			_verticalAxisRenderer.heightLimit =
				verticalAxisRatio * unscaledWidth;
		}

		_horizontalAxisRenderer.setActualSize(
			unscaledWidth - paddingLeft - paddingRight,
			unscaledHeight - paddingTop - paddingBottom);

		_horizontalAxisRenderer.move(paddingLeft, paddingTop);

		_verticalAxisRenderer.setActualSize(
			unscaledWidth - paddingLeft - paddingRight,
			unscaledHeight - paddingTop - paddingBottom);

		_verticalAxisRenderer.move(paddingLeft, paddingTop);

		if (_horizontalAxisRenderer2)
		{
			_horizontalAxisRenderer2.setActualSize(
				unscaledWidth - paddingLeft - paddingRight,
				unscaledHeight - paddingTop - paddingBottom);

			_horizontalAxisRenderer2.move(paddingLeft, paddingTop);

			// Make sure their placement aligns.
			var harPlacement:String = _horizontalAxisRenderer.placement;
			switch (harPlacement)
			{
				case "left":
				case "bottom":
				{
					_horizontalAxisRenderer2.placement = "right";
					break;
				}
				case "top":
				case "right":
				{
					_horizontalAxisRenderer2.placement = "left";
					break;
				}
			}
		}
		
		if (_verticalAxisRenderer2)
		{
			_verticalAxisRenderer2.setActualSize(
				unscaledWidth - paddingLeft - paddingRight,
				unscaledHeight - paddingTop - paddingBottom);

			_verticalAxisRenderer2.move(paddingLeft, paddingTop);

			// Make sure their placement aligns.
			var varPlacement:String = _verticalAxisRenderer.placement;
			switch (varPlacement)
			{
				case "left":
				case "bottom":
				{
					_verticalAxisRenderer2.placement = "top";
					break;
				}
				case "top":
				case "right":
				{
					_verticalAxisRenderer2.placement = "bottom";
					break;
				}
			}
		}

		_computedGutters = new Rectangle();
		
		var adjustable:Object = {};
		if (gutterLeft != null)
		{
			_computedGutters.left = Number(gutterLeft);
			adjustable.left = false;
		}
		if (gutterRight != null)
		{
			_computedGutters.right = Number(gutterRight);
			adjustable.right = false;
		}
		if (gutterTop != null)
		{
			_computedGutters.top = Number(gutterTop);
			adjustable.top = false;
		}
		if (gutterBottom != null)
		{
			_computedGutters.bottom = Number(gutterBottom);
			adjustable.bottom = false;
		}

		_computedGutters = _verticalAxisRenderer.adjustGutters(
								_computedGutters, adjustable);

		if (_verticalAxisRenderer2)
		{
			_computedGutters= _verticalAxisRenderer2.adjustGutters(
									_computedGutters, adjustable);
		}

		if (_horizontalAxisRenderer2)
		{
			_computedGutters= _horizontalAxisRenderer2.adjustGutters(
									_computedGutters, adjustable);
		}

		_computedGutters = _horizontalAxisRenderer.adjustGutters(
									_computedGutters, adjustable);

		_verticalAxisRenderer.gutters = _computedGutters;
		
		if (_verticalAxisRenderer2)
			_verticalAxisRenderer2.gutters = _computedGutters;
		
		if (_horizontalAxisRenderer2)
			_horizontalAxisRenderer2.gutters = _computedGutters;

		_transformBounds = new Rectangle(
			_computedGutters.left + paddingLeft,
			_computedGutters.top + paddingTop,
			unscaledWidth - _computedGutters.right - paddingRight -
			(_computedGutters.left + paddingLeft),
			unscaledHeight - _computedGutters.bottom - paddingBottom -
			(_computedGutters.top + paddingTop));
		
		var n:int;
		var i:int;

		if (_transforms)
		{
			for (i = 0; i < _transforms.length; i++)
			{
				_transforms[i].pixelWidth = _transformBounds.width;
				_transforms[i].pixelHeight = _transformBounds.height;
			}
		}
		
		n = allElements.length;
		for (i = 0; i < n; i++)
		{
			var c:DisplayObject = allElements[i];
			if (c is UIComponent)
			{
				(c as UIComponent).setActualSize(_transformBounds.width,
												 _transformBounds.height);
			}
			else
			{
				c.width = _transformBounds.width;
				c.height = _transformBounds.height;
			}
		}
		
		if (_seriesHolder.mask != null)
		{
			_seriesHolder.mask.width = _transformBounds.width;
			_seriesHolder.mask.height = _transformBounds.height;
		}
		
		if (_backgroundElementHolder.mask != null)
		{
			_backgroundElementHolder.mask.width = _transformBounds.width;
			_backgroundElementHolder.mask.height = _transformBounds.height;
		}
		
		if (_annotationElementHolder.mask != null)
		{
			_annotationElementHolder.mask.width = _transformBounds.width;
			_annotationElementHolder.mask.height = _transformBounds.height;
		}
		
		_seriesHolder.x = _transformBounds.left
		_seriesHolder.y = _transformBounds.top;

		_backgroundElementHolder.move(_transformBounds.left,
									  _transformBounds.top);

		_annotationElementHolder.move(_transformBounds.left,
									  _transformBounds.top);

		_bAxisLayoutDirty = false;
	}

	/**
	 *  Initializes the chart for displaying a second series.
	 *  This function is called automatically whenever any of the secondary
	 *  properties, such as <code>secondSeries</code> or
	 *  <code>secondHorizontalAxis</code>, are set.
	 *  Specific chart subtypes override this method
	 *  to initialize default secondary values.
	 *  Column charts, for example, initialize a separate secondary vertical
	 *  axis, but leave the primary and secondary horizontal axes linked.
	 */	
	protected function initSecondaryMode():void
	{
		_bDualMode = true;

		transforms = [ _transforms[0], new CartesianTransform() ];
	}

	/**
	 *  Retrieves axis instance for a particular secondary dimension
	 *  of the chart.
	 *  This is a low level accessor.
	 *  You typically retrieve the axis directly through a named property
	 *  (such as the <code>secondHorizontalAxis</code> or
	 *  <code>secondVerticalAxis</code> property).
	 */
	public function getSecondAxis(dimension:String):IAxis
	{
		return transforms[0].getAxis(dimension);
	}
	/**
	 *  Assigns an axis instance to a particular secondary dimension
	 *  of the chart.
	 *  This is a low level accessor.
	 *  You typically set the axis directly through a named property
	 *  (such as the <code>secondHorizontalAxis</code> or
	 *  <code>secondVerticalAxis</code> property).
	 */
	public function setSecondAxis(dimension:String, value:IAxis):void
	{
		transforms[0].setAxis(dimension, value);
	}
}

}
